package at.ac.tuwien.vpsbcm.spielzeugfabrik.space;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Timer;

import org.mozartspaces.capi3.AnyCoordinator;
import org.mozartspaces.capi3.FifoCoordinator;
import org.mozartspaces.capi3.LabelCoordinator;
import org.mozartspaces.capi3.LindaCoordinator;
import org.mozartspaces.core.Capi;
import org.mozartspaces.core.ContainerReference;
import org.mozartspaces.core.DefaultMzsCore;
import org.mozartspaces.core.Entry;
import org.mozartspaces.core.MzsConstants;
import org.mozartspaces.core.MzsCoreException;
import org.mozartspaces.core.TransactionReference;
import org.mozartspaces.notifications.Notification;
import org.mozartspaces.notifications.NotificationListener;
import org.mozartspaces.notifications.NotificationManager;
import org.mozartspaces.notifications.Operation;

import at.ac.tuwien.vpsbcm.spielzeugfabrik.BauteilEnum;
import at.ac.tuwien.vpsbcm.spielzeugfabrik.space.utils.ContainerUtils;

public class Benchmark implements Runnable, NotificationListener {
	private URI spaceurl;
	private volatile boolean goOn;
	private volatile boolean checkForNew; 
	private DefaultMzsCore core;
	private Capi capi;
	private ContainerUtils cUtil;
	private NotificationManager notifManager;
	private ContainerReference BauteilContainer;
	private ContainerReference BenchmarkContainer;
	private ContainerReference AusgeliefertContainer;
	private Timer t;

	private long starttime;
	
	public Benchmark (URI spaceurl) {
		this.spaceurl = spaceurl;
		this.goOn = true;
		this.checkForNew = false;
		this.starttime = 0;
		try {
			core = DefaultMzsCore.newInstanceWithoutSpace();
			capi = new Capi(core);
			cUtil = new ContainerUtils (capi, spaceurl);
			BauteilContainer = cUtil.InitBauteileContainer();
			AusgeliefertContainer = cUtil.InitAusgeliefertContainer();
			BenchmarkContainer = cUtil.InitBenchmarkContainer();
			notifManager = new NotificationManager(core);
			
		} catch (MzsCoreException e) {
			this.goOn = false;
			e.printStackTrace();
		}
	}

	public void run() {
		try {
			ArrayList<Entry> entries = new ArrayList<Entry>();
			// Prepare resoures
			for (int i=0; i<2000; i++) {
				entries.add(new Entry (new Bauteil(0, i, BauteilEnum.HOLZGEHAEUSE, false, false, "", null), LindaCoordinator.newCoordinationData(), FifoCoordinator.newCoordinationData(), AnyCoordinator.newCoordinationData("unlackierte_gehaeuse")));
				entries.add(new Entry (new Bauteil(1, i, BauteilEnum.SCHORNSTEIN, false, false, "", null), LindaCoordinator.newCoordinationData(), FifoCoordinator.newCoordinationData(), AnyCoordinator.newCoordinationData("unlackierte_schornsteine")));
			}
			for (int i=0; i<12000; i++) {
				entries.add(new Entry (new Bauteil(0, i, BauteilEnum.RAD, false, false, "", null), LindaCoordinator.newCoordinationData(), FifoCoordinator.newCoordinationData(), AnyCoordinator.newCoordinationData("RAD")));							
			}
			capi.write(entries, BauteilContainer, 0, null);
			System.out.println("Finished creating Bauteile. Starting Benchmark.");
			// notify all, that benchmark started.
			starttime = System.nanoTime();
			Entry entry = new Entry(starttime, LabelCoordinator.newCoordinationData("time"));
			TransactionReference tx = capi.createTransaction(2000, this.spaceurl);
			capi.write(entry, BenchmarkContainer, 0, tx);
			t = new Timer ( 60*1000
					            , new ActionListener () {
				                      public void actionPerformed(ActionEvent evt) {
				                    	  goOn = false;
				                    	  setCheckForNew();
				                      }
				                  }
			                    );
			t.start();
			// now, register notification
			notifManager.createNotification(BenchmarkContainer, this, Operation.WRITE, Operation.DELETE, Operation.TAKE);
			capi.commitTransaction(tx);
			while (this.goOn) {
				waitCheckForNew();
				Thread.yield();
			}
			// get time elapsed
			long endtime = System.nanoTime();
			long gottime = (Long) capi.read(BenchmarkContainer, LabelCoordinator.newSelector("time", 1), 0, null).get(0);
			System.out.format("Benchmark finished after %d nanoseconds (%d)", endtime - this.starttime, gottime - this.starttime);
			long anz = capi.test (AusgeliefertContainer, FifoCoordinator.newSelector(MzsConstants.Selecting.COUNT_ALL), 0, null);
			System.out.format(" number of finished Loks: %d\n", anz);

		} catch (MzsCoreException e) {
			
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public boolean isRunning () {
		return this.goOn;
	}
	
	public void stop () {
		this.goOn = false;
	}

	private synchronized void waitCheckForNew () {
		while (!this.checkForNew) {
			try {
				wait();
			} catch (InterruptedException e) {}
		}
		this.checkForNew = false;
	}
	
	public synchronized void setCheckForNew () {
		this.checkForNew = true;
		notifyAll();
	}
	
	public void entryOperationFinished(Notification source,
			Operation operation, List<? extends Serializable> entries) {
		Entry entry = (Entry)entries.get(0);
		Long gottime = (Long)entry.getValue();
		if (gottime.longValue() != this.starttime) {
			this.goOn = false;
			setCheckForNew();
		}
	}
	
	
	public static void main(String[] args) {
		URI spaceurl;
		Benchmark benchi;
		int c;
		boolean enable_logging = true;
		BufferedReader br = new BufferedReader (new InputStreamReader(System.in));
		if (args.length >= 1) {
			try {
				spaceurl = new URI(args[0]);
				benchi = new Benchmark(spaceurl);
				Thread t = new Thread(benchi);
				if (enable_logging) {
					System.out.println("Starte benchmark - Abbruch mit 'q'");
				}
	            t.start();
	            c = br.read();
	            while (c != 'q' && c != 'Q' && benchi.isRunning()) {
	            	c = br.read();
	            }
	            benchi.stop();
	            //benchi.setCheckForNew();
			} catch (URISyntaxException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		} else {
			System.out.println("Brauche Space-URI Kommandozeilenparameter!");		
		}
	}
}
